<!DOCTYPE html><html lang="en" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"><title>MySQL 日志 | 戴涛的宝藏</title><meta name="author" content="戴涛"><meta name="copyright" content="戴涛"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="日志三大日志 undo log（回滚日志）：是 Innodb 存储引擎层生成的日志，实现了事务中的原子性，主要用于事务回滚和 MVCC。 redo log（重做日志）：是 Innodb 存储引擎层生成的日志，实现了事务中的持久性，主要用于掉电等故障恢复； binlog （归档日志）：是 Server 层生成的日志，主要用于数据备份和主从复制；  undo log和redo logundo log">
<meta property="og:type" content="article">
<meta property="og:title" content="MySQL 日志">
<meta property="og:url" content="https://declan1.gitee.io/2023/03/23/MySQL-%E6%97%A5%E5%BF%97/index.html">
<meta property="og:site_name" content="戴涛的宝藏">
<meta property="og:description" content="日志三大日志 undo log（回滚日志）：是 Innodb 存储引擎层生成的日志，实现了事务中的原子性，主要用于事务回滚和 MVCC。 redo log（重做日志）：是 Innodb 存储引擎层生成的日志，实现了事务中的持久性，主要用于掉电等故障恢复； binlog （归档日志）：是 Server 层生成的日志，主要用于数据备份和主从复制；  undo log和redo logundo log">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://img.lovetao.top/img/202303152100335.jpg">
<meta property="article:published_time" content="2023-03-23T13:51:00.000Z">
<meta property="article:modified_time" content="2023-03-23T13:51:24.197Z">
<meta property="article:author" content="戴涛">
<meta property="article:tag" content="MySQL">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://img.lovetao.top/img/202303152100335.jpg"><link rel="shortcut icon" href="/img/favicon.png"><link rel="canonical" href="https://declan1.gitee.io/2023/03/23/MySQL-%E6%97%A5%E5%BF%97/index.html"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: undefined,
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: 'Copy successfully',
    error: 'Copy error',
    noSupport: 'The browser does not support'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  date_suffix: {
    just: 'Just',
    min: 'minutes ago',
    hour: 'hours ago',
    day: 'days ago',
    month: 'months ago'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false,
  percent: {
    toc: true,
    rightside: false,
  }
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'MySQL 日志',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2023-03-23 21:51:24'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
    win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
      const link = document.createElement('link')
      link.rel = 'stylesheet'
      link.href = url
      if (id) link.id = id
      link.onerror = reject
      link.onload = link.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        link.onload = link.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(link)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 6.3.0"></head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://img.lovetao.top/img/202303152100335.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">Articles</div><div class="length-num">61</div></a><a href="/tags/"><div class="headline">Tags</div><div class="length-num">9</div></a><a href="/categories/"><div class="headline">Categories</div><div class="length-num">9</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/messageboard/"><i class="fa-fw fas fa-tags"></i><span> 留言板</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 链接</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://img.lovetao.top/img/202303152113310.jpg')"><nav id="nav"><span id="blog-info"><a href="/" title="戴涛的宝藏"><span class="site-name">戴涛的宝藏</span></a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/messageboard/"><i class="fa-fw fas fa-tags"></i><span> 留言板</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 链接</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page" href="javascript:void(0);"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">MySQL 日志</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">Created</span><time class="post-meta-date-created" datetime="2023-03-23T13:51:00.000Z" title="Created 2023-03-23 21:51:00">2023-03-23</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">Updated</span><time class="post-meta-date-updated" datetime="2023-03-23T13:51:24.197Z" title="Updated 2023-03-23 21:51:24">2023-03-23</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/MySQL/">MySQL</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="MySQL 日志"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">Post View:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h2 id="日志"><a href="#日志" class="headerlink" title="日志"></a>日志</h2><h3 id="三大日志"><a href="#三大日志" class="headerlink" title="三大日志"></a>三大日志</h3><ul>
<li><strong>undo log（回滚日志）</strong>：是 Innodb 存储引擎层生成的日志，实现了事务中的<strong>原子性</strong>，主要用于事务回滚和 MVCC。</li>
<li><strong>redo log（重做日志）</strong>：是 Innodb 存储引擎层生成的日志，实现了事务中的<strong>持久性</strong>，主要用于掉电等故障恢复；</li>
<li><strong>binlog （归档日志）</strong>：是 Server 层生成的日志，主要用于<strong>数据备份和主从复制</strong>；</li>
</ul>
<h3 id="undo-log和redo-log"><a href="#undo-log和redo-log" class="headerlink" title="undo log和redo log"></a>undo log和redo log</h3><p>undo log 和 redo log 这两个日志都是 <strong>Innodb 存储引擎</strong>生成的。</p>
<h4 id="undo-log"><a href="#undo-log" class="headerlink" title="undo log"></a>undo log</h4><p>undo log 是一种用于撤销回退的日志。在事务没提交之前，MySQL 会先记录更新前的数据到 undo log 日志文件里面，当事务回滚时，可以利用 undo log 来进行回滚。<br><img src="http://jxau7124.oss-cn-shenzhen.aliyuncs.com/images/20230317_0006466865.png" alt="image.png"><br>每当 InnoDB 引擎对一条记录进行操作（修改、删除、新增）时，要把回滚时需要的信息都记录到 undo log 里，比如：</p>
<ul>
<li>在<strong>插入</strong>一条记录时，要把这条记录的主键值记下来，这样之后回滚时只需要把这个主键值对应的记录<strong>删掉</strong>就好了；</li>
<li>在<strong>删除</strong>一条记录时，要把这条记录中的内容都记下来，这样之后回滚时再把由这些内容组成的记录<strong>插入</strong>到表中就好了；</li>
<li>在<strong>更新</strong>一条记录时，要把被更新的列的旧值记下来，这样之后回滚时再把这些列<strong>更新为旧值</strong>就好了。</li>
</ul>
<h5 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h5><ul>
<li>实现事务回滚，保障事务的原子性。</li>
<li>实现MVCC(多版本并发控制)</li>
</ul>
<h6 id="版本链"><a href="#版本链" class="headerlink" title="版本链"></a>版本链</h6><p>一条记录的每一次更新操作产生的 undo log 格式都有一个** roll_pointer 指针和一个 trx_id 事务id**：</p>
<ul>
<li>通过 trx_id 可以知道该记录是被哪个事务修改的；</li>
<li>通过 roll_pointer 指针可以将这些 undo log 串成一个链表，这个链表就被称为版本链；</li>
</ul>
<p><img src="http://jxau7124.oss-cn-shenzhen.aliyuncs.com/images/20230317_0006461831.png" alt="image.png"></p>
<h6 id="MVCC（多版本并发控制）"><a href="#MVCC（多版本并发控制）" class="headerlink" title="MVCC（多版本并发控制）"></a>MVCC（多版本并发控制）</h6><p>undo log 还有一个作用，通过 ReadView + undo log 实现** MVCC（多版本并发控制）**。<br>对于「读提交」和「可重复读」隔离级别的事务来说，它们的快照读（普通 select 语句）是通过 Read View + undo log 来实现的，它们的区别在于创建 Read View 的时机不同：</p>
<ul>
<li>「读提交」隔离级别是在每个 select 都会生成一个新的 Read View</li>
<li>「可重复读」隔离级别是启动事务时生成一个 Read View，然后整个事务期间都在用这个 Read View</li>
</ul>
<p>这两个隔离级别实现是通过「事务的 Read View 里的字段」和「记录中的两个隐藏列（trx_id 和 roll_pointer）」的比对，<strong>如果不满足可见行，就会顺着 undo log 版本链里找到满足其可见性的记录</strong>，从而控制并发事务访问同一个记录时的行为，这就叫 MVCC（多版本并发控制）。</p>
<h4 id="redo-log"><a href="#redo-log" class="headerlink" title="redo log"></a>redo log</h4><h5 id="为什么需要"><a href="#为什么需要" class="headerlink" title="为什么需要"></a>为什么需要</h5><p>Buffer Pool 是提高了读写效率没错，但是问题来了，Buffer Pool 是基于内存的，而内存总是不可靠，万一断电重启，还没来得及落盘的脏页数据就会丢失。为了防止断电导致数据丢失的问题，当有一条记录需要更新的时候，InnoDB 引擎就会先更新内存（同时标记为脏页），然后将本次对这个页的修改以 redo log 的形式记录下来，这个时候更新就算完成了。</p>
<h5 id="什么是Redo-log"><a href="#什么是Redo-log" class="headerlink" title="什么是Redo log"></a>什么是Redo log</h5><p>redo log 是物理日志，记录了某个数据页做了什么修改，比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新，每当执行一个事务就会产生这样的一条或者多条物理日志。<br>redo log 是为了防止** Buffer Pool 中的脏页**丢失而设计的。在事务提交时，只要先将 redo log 持久化到磁盘即可，可以不需要等到将缓存在 Buffer Pool 里的脏页数据持久化到磁盘（先写日志，再写磁盘技术）。<br>当系统崩溃时，虽然脏页数据没有持久化，但是 redo log 已经持久化，接着 MySQL 重启后，可以根据 redo log 的内容，将所有数据恢复到最新的状态。</p>
<h5 id="被修改-undo-页面后，需要记录对应的-redo-log吗？"><a href="#被修改-undo-页面后，需要记录对应的-redo-log吗？" class="headerlink" title="被修改 undo 页面后，需要记录对应的 redo log吗？"></a>被修改 undo 页面后，需要记录对应的 redo log吗？</h5><p>需要的。<br>开启事务后，InnoDB层更新记录前，首先要记录相应的的undo log,如果是更新操作，需要把被更新的列的旧值记下来，也就是要生成一条 undo log,undo log会写入 Buffer Pool中的 Undo 页面。<br>不过，在内存中修改该 Undo 页面后，需要记录对应的 redo log。<br><strong>总结：</strong>事务开始–》记录修改–》先记录undo log  –》更新Undo页面 –》记录 redo log</p>
<h5 id="redo-log-buffer"><a href="#redo-log-buffer" class="headerlink" title="redo log buffer"></a>redo log buffer</h5><p>执行一个事务的过程中，产生的 redo log 也不是直接写入磁盘的，因为这样会产生大量的 I&#x2F;O 操作，而且磁盘的运行速度远慢于内存。<br>所以，redo log 也有自己的缓存—— <strong>redo log buffer</strong>，每当产生一条 redo log 时，会先写入到 redo log buffer，后续在持久化到磁盘。</p>
<h5 id="刷盘时机"><a href="#刷盘时机" class="headerlink" title="刷盘时机"></a>刷盘时机</h5><p>主要有下面几个时机：</p>
<ul>
<li>MySQl正常关闭时</li>
<li>redo log buffer的记录量大于redo log buffer容量的一半</li>
<li>MySQL后台线程每隔1秒，将redo log buffer刷到磁盘中</li>
<li>每次事务提交时都将缓存在redo log buffer 里的redo log 直接持久化到磁盘中（这个策略可由innodb_flush_log_at_trx_commit 参数控制）</li>
</ul>
<h6 id="innodb-flush-log-at-trx-commit参数"><a href="#innodb-flush-log-at-trx-commit参数" class="headerlink" title="innodb_flush_log_at_trx_commit参数"></a>innodb_flush_log_at_trx_commit参数</h6><ul>
<li><strong>单独执行一个更新语句的时候</strong>，生成的 redo log 先写入到 redo log buffer 中</li>
<li><strong>然后等事务提交的时候</strong>，再将缓存在 redo log buffer 中的 redo log 按组的方式「顺序写」到磁盘。</li>
</ul>
<p>除此之外，InnoDB 还提供了另外两种策略，由参数 innodb_flush_log_at_trx_commit 参数控制，可取的值有：0、1、2，默认值为 1，这三个值分别代表的策略如下：</p>
<ul>
<li>当设置该<strong>参数为 0 时</strong>，表示每次事务提交时 ，还是<strong>将 redo log 留在 redo log buffer 中</strong> ，该模式下在事务提交时<strong>不会主动触发</strong>写入磁盘的操作。</li>
<li>当设置该<strong>参数为 1 时</strong>，表示每次事务提交时，都<strong>将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘</strong>，这样可以保证 MySQL 异常重启之后数据不会丢失。</li>
<li>当设置该<strong>参数为 2 时</strong>，表示每次事务提交时，都只是缓存在 redo log buffer 里的 redo log **写到 **操作系统的文件系统中的 Page Cache。</li>
</ul>
<p>注意，参数为0和2时，刷入时机主要由MySQL的后台线程控制<br><img src="http://jxau7124.oss-cn-shenzhen.aliyuncs.com/images/20230317_0006472508.png" alt="image.png"></p>
<blockquote>
<p>InnoDB 的后台线程每隔 1 秒：</p>
<ul>
<li>针对参数 0 ：会把缓存在 redo log buffer 中的 redo log ，通过调用 write() 写到操作系统的 Page Cache，然后调用 fsync() 持久化到磁盘。<strong>所以参数为 0 的策略，MySQL 进程的崩溃会导致上一秒钟所有事务数据的丢失;</strong></li>
<li>针对参数 2 ：调用 fsync，将缓存在操作系统中 Page Cache 里的 redo log 持久化到磁盘。所以参数为 2 的策略，较取值为 0 情况下更安全，<strong>因为 MySQL 进程的崩溃并不会丢失数据，只有在操作系统崩溃或者系统断电的情况下</strong>，上一秒钟所有事务数据才可能丢失。</li>
</ul>
</blockquote>
<h5 id="redo-log-文件写满了怎么办？"><a href="#redo-log-文件写满了怎么办？" class="headerlink" title="redo log 文件写满了怎么办？"></a>redo log 文件写满了怎么办？</h5><p>redo log 是循环写的方式，相当于一个环形，InnoDB 用 write pos 表示 redo log 当前记录写到的位置，用 checkpoint 表示当前要擦除的位置，如下图：<br><img src="http://jxau7124.oss-cn-shenzhen.aliyuncs.com/images/20230317_0006482770.png" alt="image.png"></p>
<p>说明：<br><img src="http://jxau7124.oss-cn-shenzhen.aliyuncs.com/images/20230317_0006492757.png" alt="image.png"></p>
<h4 id="redo-log-和-undo-log-区别在哪？"><a href="#redo-log-和-undo-log-区别在哪？" class="headerlink" title="redo log 和 undo log 区别在哪？"></a>redo log 和 undo log 区别在哪？</h4><ul>
<li>redo log 记录了此次事务<strong>「完成后」</strong>的数据状态，记录的是<strong>更新之后</strong>的值；</li>
<li>undo log 记录了此次事务<strong>「开始前」</strong>的数据状态，记录的是<strong>更新之前</strong>的值；</li>
</ul>
<p><strong>事务提交之前</strong>发生了崩溃，重启后会通过 undo log 回滚事务，<strong>事务提交之后</strong>发生了崩溃，重启后会通过 redo log 恢复事务，如下图：<br><img src="http://jxau7124.oss-cn-shenzhen.aliyuncs.com/images/20230317_0006493375.png" alt="image.png"></p>
<h4 id="redo-log-amp-undo-log作用，什么时候生成"><a href="#redo-log-amp-undo-log作用，什么时候生成" class="headerlink" title="redo log&amp;undo log作用，什么时候生成"></a>redo log&amp;undo log作用，什么时候生成</h4><h5 id="redo-log作用"><a href="#redo-log作用" class="headerlink" title="redo log作用"></a>redo log作用</h5><ul>
<li>实现事务的持久性，让 MySQL 有 crash-safe 的能力，能够保证 MySQL 在任何时间段突然崩溃，重启后之前已提交的记录都不会丢失；</li>
<li>将写操作从「随机写」变成了「顺序写」，提升 MySQL 写入磁盘的性能。</li>
</ul>
<h5 id="redo-log生成时机"><a href="#redo-log生成时机" class="headerlink" title="redo log生成时机"></a>redo log生成时机</h5><p>开启事务后，InnoDB层更新记录前，首先要记录相应的的undo log,如果是更新操作，需要把被更新的列的旧值记下来，也就是要生成一条 undo log,undo log会写入 Buffer Pool中的 Undo 页面。<br>不过，在内存中修改该 Undo 页面后，需要记录对应的 redo log。</p>
<h5 id="undo-log作用"><a href="#undo-log作用" class="headerlink" title="undo log作用"></a>undo log作用</h5><ul>
<li>实现事务回滚，保障事务的原子性。事务处理过程中，如果出现了错误或者用户执 行了 ROLLBACK 语句，MySQL 可以利用 undo log 中的历史数据将数据恢复到事务开始之前的状态。</li>
<li>实现MVCC(多版本并发控制)。MVCC 是通过 ReadView + undo log 实现的。undo log 为每条记录保存多份历史数据，MySQL 在执行快照读（普通 select 语句）的时候，会根据事务的 Read View 里的信息，顺着 undo log 的版本链找到满足其可见性的记录。</li>
</ul>
<h5 id="undo-log生成时机"><a href="#undo-log生成时机" class="headerlink" title="undo log生成时机"></a>undo log生成时机</h5><p>开启事务后，InnoDB 层更新记录前，首先要记录相应的 undo log，如果是更新操作，需要把被更新的列的旧值记下来。每当 InnoDB 引擎对一条记录进行操作（修改、删除、新增）时，要把回滚时需要的信息都记录到 undo log 里，比如：</p>
<ul>
<li>在插入一条记录时，要把这条记录的主键值记下来，这样之后回滚时只需要把这个主键值对应的记录删掉就好了；</li>
<li>在删除一条记录时，要把这条记录中的内容都记下来，这样之后回滚时再把由这些内容组成的记录插入到表中就好了；</li>
<li>在更新一条记录时，要把被更新的列的旧值记下来，这样之后回滚时再把这些列更新为旧值就好了。</li>
</ul>
<h3 id="bin-log"><a href="#bin-log" class="headerlink" title="bin log"></a>bin log</h3><h4 id="为什么需要？"><a href="#为什么需要？" class="headerlink" title="为什么需要？"></a>为什么需要？</h4><p>binlog 文件是记录了所有数据库表结构变更和表数据修改的日志，不会记录查询类的操作，比如 SELECT 和 SHOW 操作。<br>产生时机<br>MySQL 在完成一条更新操作后，Server 层还会生成一条 binlog，等之后事务提交的时候，会将该事物执行过程中产生的所有 binlog 统一写 入 binlog 文件。</p>
<h4 id=""><a href="#" class="headerlink" title=""></a></h4><p>redo log 和 binlog 有什么区别？</p>
<ol>
<li>生成对象不同</li>
</ol>
<p>redo log是物理层面的日志<br>bin log 是MySQL服务层面的日志</p>
<ol start="2">
<li>写入格式不同</li>
</ol>
<ul>
<li>bin log有3种格式:STATEMENT、ROW、MIXED<ul>
<li>STATEMENT，存储完整的SQL语句，占用体积小，但是bin log主要用于主从复制的，对于那些含有动态函数的sql语句，例如NOW()，在主库上执行的结果并不是你在从库执行的结果，这种随时在变的函数会导致复制的数据不一致；</li>
<li>ROW，存储的是每一行变化的记录，它可以解决STATEMENT的缺点，但是当一些范围SQL执行的时候，例如update操作，会把更改记录的行都写入日志，造成冗余使 binlog 文件过大</li>
<li>MIXED，对于含有动态函数的sql语句使用ROW格式记录，其他普通SQL语句使用STATEMENT格式</li>
</ul>
</li>
<li>redo log记录的是在数据页做了什么修改，比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新；</li>
</ul>
<ol start="3">
<li>用途不同</li>
</ol>
<ul>
<li>bin log主要用于备份恢复、主从复制</li>
<li>redo log主要用于crash-safe</li>
</ul>
<ol start="4">
<li>写入方式</li>
</ol>
<ul>
<li>binlog 是追加写，写满一个文件，就创建一个新的文件继续写，不会覆盖以前的日志，保存的是全量的日志。</li>
<li>redo log 是循环写，日志空间大小是固定，全部写满就从头开始，保存未被刷入磁盘的脏页日志。</li>
</ul>
<ol start="5">
<li>写入时间不同</li>
</ol>
<ul>
<li>二进制文件仅在事务提交前进行提交，只写磁盘一次</li>
<li>在事务执行的过程中，却不断有重做日志被写入到重做日志文件组里</li>
</ul>
<h4 id="如果不小心整个数据库的数据被删除了，能使用-redo-log-文件恢复数据吗？"><a href="#如果不小心整个数据库的数据被删除了，能使用-redo-log-文件恢复数据吗？" class="headerlink" title="如果不小心整个数据库的数据被删除了，能使用 redo log 文件恢复数据吗？"></a>如果不小心整个数据库的数据被删除了，能使用 redo log 文件恢复数据吗？</h4><p>因为 redo log 文件是循环写，是会边写边擦除日志的，只记录未被刷入磁盘的数据的物理日志，已经刷入磁盘的数据都会从 redo log 文件里擦除。<br>binlog 文件保存的是全量的日志，也就是保存了所有数据变更的情况，理论上只要记录在 binlog 上的数据，都可以恢复，所以如果不小心整个数据库的数据被删除了，得用 binlog 文件恢复数据。</p>
<h4 id="主从复制是怎么实现？"><a href="#主从复制是怎么实现？" class="headerlink" title="主从复制是怎么实现？"></a>主从复制是怎么实现？</h4><p>MySQL 的主从复制依赖于 binlog ，也就是记录 MySQL 上的所有变化并以二进制形式保存在磁盘上。复制的过程就是将 binlog 中的数据从主库传输到从库上。<br>MySQL 集群的主从复制过程梳理成 3 个阶段：</p>
<ul>
<li><strong>写入 Binlog</strong>：主库先写 binlog 日志，再提交事务，并更新本地存储数据。</li>
<li><strong>同步 Binlog</strong>：从库会创建一个专门的 I&#x2F;O 线程，连接主库的 log dump 线程，把 binlog 复制到所有从库上，每个从库把 binlog 写到暂存日志（中继日志）中。</li>
<li><strong>回放 Binlog</strong>：回放 binlog，并更新存储引擎中的数据。</li>
</ul>
<p><img src="http://jxau7124.oss-cn-shenzhen.aliyuncs.com/images/20230317_0006509671.png" alt="image.png"></p>
<h4 id="binlog-什么时候刷盘？"><a href="#binlog-什么时候刷盘？" class="headerlink" title="binlog 什么时候刷盘？"></a>binlog 什么时候刷盘？</h4><p>在事务提交的时候，执行器把 binlog cache 里的完整事务写入到 binlog 文件中，并清空 binlog cache。<br><strong>binlog cache</strong><br>MySQL 给每个线程分配了一片内存用于缓冲 binlog ，该内存叫 binlog cache，参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。如果超过了这个参数规定的大小，就要暂存到磁盘。<br><img src="http://jxau7124.oss-cn-shenzhen.aliyuncs.com/images/20230317_0006508064.png" alt="image.png"></p>
<p>虽然每个线程有自己 binlog cache，但是最终都写到同一个 binlog 文件：</p>
<ul>
<li>图中的 write，指的就是指把日志写入到 binlog 文件，但是并没有把数据持久化到磁盘，因为数据还缓存在文件系统的 page cache 里，write 的写入速度还是比较快的，因为不涉及磁盘 I&#x2F;O。</li>
<li>图中的 fsync，才是将数据持久化到磁盘的操作，这里就会涉及磁盘 I&#x2F;O，所以频繁的 fsync 会导致磁盘的 I&#x2F;O 升高。</li>
</ul>
<h5 id="sync-binlog参数"><a href="#sync-binlog参数" class="headerlink" title="sync_binlog参数"></a>sync_binlog参数</h5><p>MySQL提供一个 sync_binlog 参数来控制数据库的 binlog 刷到磁盘上的频率：</p>
<ul>
<li>sync_binlog &#x3D; 0 的时候，表示每次提交事务<strong>都只 write，不 fsync</strong>，后续交由操作系统决定何时将数据持久化到磁盘；</li>
<li>sync_binlog &#x3D; 1 的时候，表示每次提交事务<strong>都会 write，然后马上执行 fsync</strong>；</li>
<li>sync_binlog &#x3D;N(N&gt;1) 的时候，表示每次提交事务<strong>都 write，但累积 N 个事务后才 fsync。</strong></li>
</ul>
<h3 id="小结——update-语句的执行过程"><a href="#小结——update-语句的执行过程" class="headerlink" title="小结——update 语句的执行过程"></a>小结——update 语句的执行过程</h3><p>具体更新一条记录 UPDATE t_user SET name &#x3D; ‘xiaolin’ WHERE id &#x3D; 1; 的流程如下:</p>
<ol>
<li>执行器负责具体执行，会调用存储引擎的接口，通过主键索引树搜索获取 id &#x3D; 1 这一行记录：<ul>
<li>如果 id&#x3D;1 这一行所在的数据页本来就在 buffer pool 中，就直接返回给执行器更新；</li>
<li>如果记录不在 buffer pool，将数据页从磁盘读入到 buffer pool，返回记录给执行器。</li>
</ul>
</li>
<li>执行器得到聚簇索引记录后，会看一下更新前的记录和更新后的记录是否一样：<ul>
<li>如果一样的话就不进行后续更新流程；</li>
<li>如果不一样的话就把更新前的记录和更新后的记录都当作参数传给 InnoDB 层，让 InnoDB 真正的执行更新记录的操作；</li>
</ul>
</li>
<li>开启事务， InnoDB 层更新记录前，首先要记录相应的 undo log，因为这是更新操作，需要把被更新的列的旧值记下来，也就是要生成一条 undo log，undo log 会写入 Buffer Pool 中的 Undo 页面，不过在内存修改该 Undo 页面后，需要记录对应的 redo log。</li>
<li>InnoDB 层开始更新记录（更新的是内存里的记录），会先更新内存（同时标记为脏页），然后将记录写到 redo log 里面，这个时候更新就算完成了。为了减少磁盘I&#x2F;O，不会立即将脏页写入磁盘，后续由后台线程选择一个合适的时机将脏页写入到磁盘。这就是 <strong>WAL 技术</strong>，MySQL 的写操作并不是立刻写到磁盘上，而是先写 redo 日志，然后在合适的时间再将修改的行数据写到磁盘上。</li>
<li>至此，一条记录更新完了。</li>
<li>在一条更新语句执行完成后，然后开始记录该语句对应的 binlog，此时记录的 binlog 会被保存到 binlog cache，并没有刷新到硬盘上的 binlog 文件，在事务提交时才会统一将该事务运行过程中的所有 binlog 刷新到硬盘。</li>
<li>事务提交</li>
</ol>
<h3 id="两阶段提交"><a href="#两阶段提交" class="headerlink" title="两阶段提交"></a>两阶段提交</h3><p>事务提交后，redo log 和 binlog 都要持久化到磁盘，但是这两个是独立的逻辑，可能出现半成功的状态，这样就造成两份日志之间的逻辑不一致。</p>
<h4 id="为什么需要-1"><a href="#为什么需要-1" class="headerlink" title="为什么需要"></a>为什么需要</h4><ul>
<li><strong>如果在将 redo log 刷入到磁盘之后， MySQL 突然宕机了，而 binlog 还没有来得及写入</strong>。MySQL 重启后，通过 redo log 能将 Buffer Pool 中 id &#x3D; 1 这行数据的 name 字段恢复到新值 xiaolin，但是 binlog 里面没有记录这条更新语句，在主从架构中，binlog 会被复制到从库，由于 binlog 丢失了这条更新语句，从库的这一行 name 字段是旧值 jay，与主库的值不一致性；</li>
<li><strong>如果在将 binlog 刷入到磁盘之后， MySQL 突然宕机了，而 redo log 还没有来得及写入</strong>。由于 redo log 还没写，崩溃恢复以后这个事务无效，所以 id &#x3D; 1 这行数据的 name 字段还是旧值 jay，而 binlog 里面记录了这条更新语句，在主从架构中，binlog 会被复制到从库，从库执行了这条更新语句，那么这一行 name 字段是新值 xiaolin，与主库的值不一致性；</li>
</ul>
<h4 id="提交的过程"><a href="#提交的过程" class="headerlink" title="提交的过程"></a>提交的过程</h4><p>在 MySQL 的 InnoDB 存储引擎中，开启 binlog 的情况下，MySQL 会同时维护 binlog 日志与 InnoDB 的 redo log，为了保证这两个日志的一致性，MySQL 使用了<strong>内部 XA 事务</strong>，内部 XA 事务由 binlog 作为协调者，存储引擎是参与者。<br>当客户端执行 commit 语句或者在自动提交的情况下，MySQL 内部开启一个 XA 事务，<strong>分两阶段来完成 XA 事务的提交</strong>，如下图：<br><img src="http://jxau7124.oss-cn-shenzhen.aliyuncs.com/images/20230317_0006513366.png" alt="image.png"><br>就是<strong>将 redo log 的写入拆成了两个步骤：prepare 和 commit，中间再穿插写入binlog</strong>，具体如下：</p>
<ul>
<li><strong>prepare 阶段</strong>：将 XID（内部 XA 事务的 ID） 写入到 redo log，同时将 redo log 对应的事务状态设置为 prepare，然后将 redo log 持久化到磁盘（innodb_flush_log_at_trx_commit &#x3D; 1 的作用）；</li>
<li><strong>commit 阶段</strong>：把 XID 写入到 binlog，然后将 binlog 持久化到磁盘（sync_binlog &#x3D; 1 的作用），接着调用引擎的提交事务接口，将 redo log 状态设置为 commit，此时该状态并不需要持久化到磁盘，只需要 write 到文件系统的 page cache 中就够了，因为只要 binlog 写磁盘成功，就算 redo log 的状态还是 prepare 也没有关系，一样会被认为事务已经执行成功；</li>
</ul>
<h5 id="重启过程"><a href="#重启过程" class="headerlink" title="重启过程"></a>重启过程</h5><p>在 MySQL 重启后会按顺序扫描 redo log 文件，碰到处于 prepare 状态的 redo log，就拿着 redo log 中的 XID 去 binlog 查看是否存在此 XID：</p>
<ul>
<li><strong>如果 binlog 中没有当前内部 XA 事务的 XID，说明 redolog 完成刷盘，但是 binlog 还没有刷盘，则回滚事务</strong>。对应时刻 A 崩溃恢复的情况。</li>
<li><strong>如果 binlog 中有当前内部 XA 事务的 XID，说明 redolog 和 binlog 都已经完成了刷盘，则提交事务</strong>。对应时刻 B 崩溃恢复的情况。</li>
</ul>
<h5 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h5><p>可以看到，<strong>对于处于 prepare 阶段的 redo log，即可以提交事务，也可以回滚事务，这取决于是否能在 binlog 中查找到与 redo log 相同的 XID</strong>，如果有就提交事务，如果没有就回滚事务。这样就可以保证 redo log 和 binlog 这两份日志的一致性了。<br>所以说，<strong>两阶段提交是以 binlog 写成功为事务提交成功的标识</strong>，因为 binlog 写成功了，就意味着能在 binlog 中查找到与 redo log 相同的 XID。</p>
<h4 id="出现的问题"><a href="#出现的问题" class="headerlink" title="出现的问题"></a>出现的问题</h4><p>两阶段提交虽然保证了两个日志文件的数据一致性，但是性能很差，主要有两个方面的影响：</p>
<ul>
<li><strong>磁盘 I&#x2F;O 次数高</strong>：对于“双1”配置，每个事务提交都会进行两次 fsync（刷盘），一次是 redo log 刷盘，另一次是 binlog 刷盘。相当于一次事务，两次磁盘IO操作。</li>
<li><strong>锁竞争激烈</strong>：两阶段提交虽然能够保证「单事务」两个日志的内容一致，但在「多事务」的情况下，却不能保证两者的提交顺序一致，因此，在两阶段提交的流程基础上，还需要加一个锁来保证提交的原子性，从而保证多事务的情况下，两个日志的提交顺序一致。</li>
</ul>
<h4 id="两段锁协议，分布式事务中两阶段提交"><a href="#两段锁协议，分布式事务中两阶段提交" class="headerlink" title="两段锁协议，分布式事务中两阶段提交"></a>两段锁协议，分布式事务中两阶段提交</h4><p>分布式事务使用两段式提交(two- phase commit)的方式。在第一阶段,所有参与全局事务的节点都开始准备( PREPARE),告诉事务管理器它们准备好提交了。在第二阶段,事务管理器告诉资源管理器执行 ROLLBACK还是 COMMIT。如果任何一个节点显示不能提交,则所有的节点都被告知需要回滚。可见与本地事务不同的是,分布式事务需要多一次的 PREPARE操作,待收到所有节点的同意信息后,再进行 COMMIT或是ROLLBACK操作。</p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">Author: </span><span class="post-copyright-info"><a href="https://declan1.gitee.io">戴涛</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">Link: </span><span class="post-copyright-info"><a href="https://declan1.gitee.io/2023/03/23/MySQL-%E6%97%A5%E5%BF%97/">https://declan1.gitee.io/2023/03/23/MySQL-%E6%97%A5%E5%BF%97/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">Copyright Notice: </span><span class="post-copyright-info">All articles in this blog are licensed under <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0</a> unless stating additionally.</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/MySQL/">MySQL</a></div><div class="post_share"><div class="social-share" data-image="https://img.lovetao.top/img/202303152100335.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2023/03/23/B-%E6%A0%91/" title="B+树"><div class="cover" style="background: var(--default-bg-color)"></div><div class="pagination-info"><div class="label">Previous Post</div><div class="prev_info">B+树</div></div></a></div><div class="next-post pull-right"><a href="/2023/03/23/MySQL-%E9%94%81%E6%9C%BA%E5%88%B6/" title="MySQL 锁机制"><div class="cover" style="background: var(--default-bg-color)"></div><div class="pagination-info"><div class="label">Next Post</div><div class="next_info">MySQL 锁机制</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>Related Articles</span></div><div class="relatedPosts-list"><div><a href="/2023/03/23/B-%E6%A0%91/" title="B+树"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-03-23</div><div class="title">B+树</div></div></a></div><div><a href="/2023/03/15/MySQL/" title="MySQL主从复制原理及可能出现的问题"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-03-15</div><div class="title">MySQL主从复制原理及可能出现的问题</div></div></a></div><div><a href="/2023/03/17/MySQL-%E4%B8%AD%E7%9A%84%E9%94%81/" title="MySQL 中的锁"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-03-17</div><div class="title">MySQL 中的锁</div></div></a></div><div><a href="/2023/03/23/MySQL-%E4%BA%8B%E5%8A%A1/" title="MySQL 事务"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-03-23</div><div class="title">MySQL 事务</div></div></a></div><div><a href="/2023/03/23/MySQL%E4%B8%ADselect%E8%AF%AD%E5%8F%A5%E5%90%8E%E9%9D%A2%E5%8A%A0for-update%E7%AD%89%E5%85%B3%E9%94%AE%E8%AF%8D%E7%9A%84%E7%94%A8%E5%A4%84%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F/" title="MySQL 中 select 语句后面加 for update 等关键词的用处是什么？"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-03-23</div><div class="title">MySQL 中 select 语句后面加 for update 等关键词的用处是什么？</div></div></a></div><div><a href="/2023/03/23/MySQL-%E9%94%81%E6%9C%BA%E5%88%B6/" title="MySQL 锁机制"><div class="cover" style="background: var(--default-bg-color)"></div><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-03-23</div><div class="title">MySQL 锁机制</div></div></a></div></div></div><hr/><div id="post-comment"><div class="comment-head"><div class="comment-headline"><i class="fas fa-comments fa-fw"></i><span> Comment</span></div></div><div class="comment-wrap"><div><div class="vcomment" id="vcomment"></div></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="https://img.lovetao.top/img/202303152100335.jpg" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">戴涛</div><div class="author-info__description">虽平凡，但不甘于平凡</div></div><div class="card-info-data site-data is-center"><a href="/archives/"><div class="headline">Articles</div><div class="length-num">61</div></a><a href="/tags/"><div class="headline">Tags</div><div class="length-num">9</div></a><a href="/categories/"><div class="headline">Categories</div><div class="length-num">9</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/xxxxxx"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://gitee.com/declan1" target="_blank" title="Github"><i class="fab fa-github"></i></a><a class="social-icon" href="mailto:coder_tao@163.com" target="_blank" title="Email"><i class="fas fa-envelope"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>Announcement</span></div><div class="announcement_content">This is my Blog</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>Catalog</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%97%A5%E5%BF%97"><span class="toc-number">1.</span> <span class="toc-text">日志</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%89%E5%A4%A7%E6%97%A5%E5%BF%97"><span class="toc-number">1.1.</span> <span class="toc-text">三大日志</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#undo-log%E5%92%8Credo-log"><span class="toc-number">1.2.</span> <span class="toc-text">undo log和redo log</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#undo-log"><span class="toc-number">1.2.1.</span> <span class="toc-text">undo log</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BD%9C%E7%94%A8"><span class="toc-number">1.2.1.1.</span> <span class="toc-text">作用</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#%E7%89%88%E6%9C%AC%E9%93%BE"><span class="toc-number">1.2.1.1.1.</span> <span class="toc-text">版本链</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#MVCC%EF%BC%88%E5%A4%9A%E7%89%88%E6%9C%AC%E5%B9%B6%E5%8F%91%E6%8E%A7%E5%88%B6%EF%BC%89"><span class="toc-number">1.2.1.1.2.</span> <span class="toc-text">MVCC（多版本并发控制）</span></a></li></ol></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#redo-log"><span class="toc-number">1.2.2.</span> <span class="toc-text">redo log</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81"><span class="toc-number">1.2.2.1.</span> <span class="toc-text">为什么需要</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFRedo-log"><span class="toc-number">1.2.2.2.</span> <span class="toc-text">什么是Redo log</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%A2%AB%E4%BF%AE%E6%94%B9-undo-%E9%A1%B5%E9%9D%A2%E5%90%8E%EF%BC%8C%E9%9C%80%E8%A6%81%E8%AE%B0%E5%BD%95%E5%AF%B9%E5%BA%94%E7%9A%84-redo-log%E5%90%97%EF%BC%9F"><span class="toc-number">1.2.2.3.</span> <span class="toc-text">被修改 undo 页面后，需要记录对应的 redo log吗？</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#redo-log-buffer"><span class="toc-number">1.2.2.4.</span> <span class="toc-text">redo log buffer</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%88%B7%E7%9B%98%E6%97%B6%E6%9C%BA"><span class="toc-number">1.2.2.5.</span> <span class="toc-text">刷盘时机</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#innodb-flush-log-at-trx-commit%E5%8F%82%E6%95%B0"><span class="toc-number">1.2.2.5.1.</span> <span class="toc-text">innodb_flush_log_at_trx_commit参数</span></a></li></ol></li><li class="toc-item toc-level-5"><a class="toc-link" href="#redo-log-%E6%96%87%E4%BB%B6%E5%86%99%E6%BB%A1%E4%BA%86%E6%80%8E%E4%B9%88%E5%8A%9E%EF%BC%9F"><span class="toc-number">1.2.2.6.</span> <span class="toc-text">redo log 文件写满了怎么办？</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#redo-log-%E5%92%8C-undo-log-%E5%8C%BA%E5%88%AB%E5%9C%A8%E5%93%AA%EF%BC%9F"><span class="toc-number">1.2.3.</span> <span class="toc-text">redo log 和 undo log 区别在哪？</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#redo-log-amp-undo-log%E4%BD%9C%E7%94%A8%EF%BC%8C%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E7%94%9F%E6%88%90"><span class="toc-number">1.2.4.</span> <span class="toc-text">redo log&amp;undo log作用，什么时候生成</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#redo-log%E4%BD%9C%E7%94%A8"><span class="toc-number">1.2.4.1.</span> <span class="toc-text">redo log作用</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#redo-log%E7%94%9F%E6%88%90%E6%97%B6%E6%9C%BA"><span class="toc-number">1.2.4.2.</span> <span class="toc-text">redo log生成时机</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#undo-log%E4%BD%9C%E7%94%A8"><span class="toc-number">1.2.4.3.</span> <span class="toc-text">undo log作用</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#undo-log%E7%94%9F%E6%88%90%E6%97%B6%E6%9C%BA"><span class="toc-number">1.2.4.4.</span> <span class="toc-text">undo log生成时机</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#bin-log"><span class="toc-number">1.3.</span> <span class="toc-text">bin log</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%EF%BC%9F"><span class="toc-number">1.3.1.</span> <span class="toc-text">为什么需要？</span></a></li><li class="toc-item toc-level-4"><a class="toc-link"><span class="toc-number">1.3.2.</span> <span class="toc-text"></span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A6%82%E6%9E%9C%E4%B8%8D%E5%B0%8F%E5%BF%83%E6%95%B4%E4%B8%AA%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E6%95%B0%E6%8D%AE%E8%A2%AB%E5%88%A0%E9%99%A4%E4%BA%86%EF%BC%8C%E8%83%BD%E4%BD%BF%E7%94%A8-redo-log-%E6%96%87%E4%BB%B6%E6%81%A2%E5%A4%8D%E6%95%B0%E6%8D%AE%E5%90%97%EF%BC%9F"><span class="toc-number">1.3.3.</span> <span class="toc-text">如果不小心整个数据库的数据被删除了，能使用 redo log 文件恢复数据吗？</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6%E6%98%AF%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0%EF%BC%9F"><span class="toc-number">1.3.4.</span> <span class="toc-text">主从复制是怎么实现？</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#binlog-%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E5%88%B7%E7%9B%98%EF%BC%9F"><span class="toc-number">1.3.5.</span> <span class="toc-text">binlog 什么时候刷盘？</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#sync-binlog%E5%8F%82%E6%95%B0"><span class="toc-number">1.3.5.1.</span> <span class="toc-text">sync_binlog参数</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B0%8F%E7%BB%93%E2%80%94%E2%80%94update-%E8%AF%AD%E5%8F%A5%E7%9A%84%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B"><span class="toc-number">1.4.</span> <span class="toc-text">小结——update 语句的执行过程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4"><span class="toc-number">1.5.</span> <span class="toc-text">两阶段提交</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81-1"><span class="toc-number">1.5.1.</span> <span class="toc-text">为什么需要</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%8F%90%E4%BA%A4%E7%9A%84%E8%BF%87%E7%A8%8B"><span class="toc-number">1.5.2.</span> <span class="toc-text">提交的过程</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E9%87%8D%E5%90%AF%E8%BF%87%E7%A8%8B"><span class="toc-number">1.5.2.1.</span> <span class="toc-text">重启过程</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%80%BB%E7%BB%93"><span class="toc-number">1.5.2.2.</span> <span class="toc-text">总结</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%87%BA%E7%8E%B0%E7%9A%84%E9%97%AE%E9%A2%98"><span class="toc-number">1.5.3.</span> <span class="toc-text">出现的问题</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%A4%E6%AE%B5%E9%94%81%E5%8D%8F%E8%AE%AE%EF%BC%8C%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E4%B8%AD%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4"><span class="toc-number">1.5.4.</span> <span class="toc-text">两段锁协议，分布式事务中两阶段提交</span></a></li></ol></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>Recent Post</span></div><div class="aside-list"><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2023/03/28/HTTP-1-1%E3%80%81HTTP-2%E3%80%81HTTP-3-%E6%BC%94%E5%8F%98/" title="HTTP/1.1、HTTP/2、HTTP/3 演变">HTTP/1.1、HTTP/2、HTTP/3 演变</a><time datetime="2023-03-28T15:30:00.000Z" title="Created 2023-03-28 23:30:00">2023-03-28</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2023/03/24/HTTPS-%E7%9A%84%E5%BA%94%E7%94%A8%E6%95%B0%E6%8D%AE%E6%98%AF%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81%E5%AE%8C%E6%95%B4%E6%80%A7%E7%9A%84%EF%BC%9F/" title="HTTPS 的应用数据是如何保证完整性的？">HTTPS 的应用数据是如何保证完整性的？</a><time datetime="2023-03-24T09:25:00.000Z" title="Created 2023-03-24 17:25:00">2023-03-24</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2023/03/24/HTTPS-%E6%98%AF%E5%A6%82%E4%BD%95%E5%BB%BA%E7%AB%8B%E8%BF%9E%E6%8E%A5%E7%9A%84%EF%BC%9F/" title="HTTPS 是如何建立连接的？">HTTPS 是如何建立连接的？</a><time datetime="2023-03-24T09:25:00.000Z" title="Created 2023-03-24 17:25:00">2023-03-24</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2023/03/24/HTTPS-%E4%B8%80%E5%AE%9A%E5%AE%89%E5%85%A8%E5%8F%AF%E9%9D%A0%E5%90%97%EF%BC%9F/" title="HTTPS 一定安全可靠吗？">HTTPS 一定安全可靠吗？</a><time datetime="2023-03-24T09:24:00.000Z" title="Created 2023-03-24 17:24:00">2023-03-24</time></div></div><div class="aside-list-item no-cover"><div class="content"><a class="title" href="/2023/03/23/%E5%AE%9E%E6%88%98/" title="实战">实战</a><time datetime="2023-03-23T13:52:00.000Z" title="Created 2023-03-23 21:52:00">2023-03-23</time></div></div></div></div></div></div></main><footer id="footer" style="background-image: url('https://img.lovetao.top/img/202303152113310.jpg')"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By 戴涛</div><div class="framework-info"><span>Framework </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>Theme </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="Read Mode"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="Switch Between Light And Dark Mode"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="Toggle between single-column and double-column"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="Setting"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="Table Of Contents"><i class="fas fa-list-ul"></i></button><a id="to_comment" href="#post-comment" title="Scroll To Comments"><i class="fas fa-comments"></i></a><button id="go-up" type="button" title="Back To Top"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><div class="js-pjax"><script>function loadValine () {
  function initValine () {
    const valine = new Valine(Object.assign({
      el: '#vcomment',
      appId: 'cEUNK0k7aOSrnE93yOEHT9io-gzGzoHsz',
      appKey: '3oME0qpmzHlf1QmZiMY9i8rs',
      avatar: 'declan',
      serverURLs: '',
      emojiMaps: "",
      path: window.location.pathname,
      visitor: false
    }, null))
  }

  if (typeof Valine === 'function') initValine() 
  else getScript('https://cdn.jsdelivr.net/npm/valine/dist/Valine.min.js').then(initValine)
}

if ('Valine' === 'Valine' || !false) {
  if (false) btf.loadComment(document.getElementById('vcomment'),loadValine)
  else setTimeout(loadValine, 0)
} else {
  function loadOtherComment () {
    loadValine()
  }
}</script></div><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>